package com.crpc.common.utils.IdGenerator;

import com.crpc.common.utils.DateUtil;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.concurrent.atomic.LongAdder;

/**
 * @author wzy
 * @version 1.0
 * @description: 雪花算法
 * @date 2023/9/29 12:20
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class IdGeneratorSnowflake implements IdGenerationStrategy {
    //单机状态下可用 ,集群不行
//    private static LongAdder longAdder=new LongAdder();
//    private static long getId(){
//        longAdder.increment();
//        return longAdder.sum();
//    }
// id
// 机房号 5byte
// 机器号 5byte
// 时间戳1970-1-1 long  原来64位,减少为32位 选择比较近的时
// 同一个机房的同一个机器号的同一个时间可以应为并发需要很多个id
// 序列号 12byte


    //====================异或和位移===========================
    //定义起始时间戳
    public static final long START_STAMP = DateUtil.get("2022-1-1").getTime();
    //机房
    public static final int DATA_CENTER_BIT = 5;
    //机器
    public static final int MACHINE_BIT = 5;
    //序列
    public static final int SEQUENCE_BIT = 12;

    //最大值
    public static final long DATA_CENTER_MAX = ~(-1L << DATA_CENTER_BIT);
    public static final long MACHINE_MAX = ~(-1L << MACHINE_BIT);
    public static final long SEQUENCE_Max = ~(-1L << SEQUENCE_BIT);


    //时间戳(42) 机房号(5) 机器号(5) 序列号(12)
    //101010101010101010101010101010101010101011 10101 10101 101010101011

    public static final long TIMESTAMP_LEFT = DATA_CENTER_BIT + MACHINE_BIT + SEQUENCE_BIT;
    public static final long DATACENTER_LEFT = MACHINE_BIT + SEQUENCE_BIT;
    public static final long MACHINE_LEFT = SEQUENCE_BIT;

    private long dataCenterId;
    private long machineId;
    private LongAdder sequenceId = new LongAdder();
    //时钟回拨的问题
    private long lastTimeStamp = -1L;

    public IdGeneratorSnowflake(long dataCenterId, long machineId) {
        //判断传入的参数是否合法
        if (dataCenterId > DATA_CENTER_MAX || machineId > MACHINE_MAX) {
            throw new IllegalArgumentException("你传入的数据中心编号或者机器号不合法");
        }
        this.dataCenterId = dataCenterId;
        this.machineId = machineId;
    }

    public static void main(String[] args) {
        System.out.println(new IdGeneratorSnowflake(1, 2).getId());
    }

    public long getId() {
        //处理时间戳的问题
        long currentTime = System.currentTimeMillis();
        long timeTemp = currentTime - START_STAMP;
        //判断时钟回拨
        if (timeTemp < lastTimeStamp) {
            throw new RuntimeException("服务器发生了时钟回拨,请联系服务器管理员");
        }
        //同一个机房同一个时间节点同一台机器 sequenceId 需要自增处理
        if (timeTemp == lastTimeStamp) {
            sequenceId.increment();
            if (sequenceId.sum() >= SEQUENCE_Max) {
                timeTemp = getNextTimesStamp();
                sequenceId.reset();
            }
        } else {
            sequenceId.reset();
        }
        //执行结束将时间戳复制给lastTimeStamp
        lastTimeStamp = timeTemp;
        long sequence = sequenceId.sum();
        return timeTemp << TIMESTAMP_LEFT | dataCenterId << DATACENTER_LEFT | machineId << MACHINE_LEFT | sequence;
    }

    private long getNextTimesStamp() {
        //获取当前的时间戳
        long current = System.currentTimeMillis() - START_STAMP;
        while (current == lastTimeStamp) {
            current = System.currentTimeMillis() - START_STAMP;
        }
        return current;
    }
}
